-- The MIT License (MIT)
-- Copyright (c) 2016 Yanghe Sun

compiler = require("compiler")
wzip     = require("wzip")

runner = require("runner")
path   = require("path")
fs     = require("path.fs")

import splitext, split, join from path

class contest
    new: =>
        @_datas = {}  -- {name, wzip} inside
        @_member = {}  -- {name, dir, ret} inside
        @_compiler = compiler!

    AddDataDir: (dir, cache = true) =>
        for v, t in fs.dir(dir)
            @AddDataDir(v, cache) if t == "dir"
            continue if t != "file" or select(2, splitext(v)) != ".wzip"
            name = splitext(select(2, split(v)))
            table.insert(@_datas, { name, wzip(join(dir, v), fs.tmpdir!, cache) })
            setmetatable(@_datas[#@_datas], { __gc: => self[2]\Clean! })

    AddSrcDir: (dir) =>
        for v, t in fs.dir(dir)
            continue if t != "dir"
            table.insert(@_member, { v, join(dir, v), {} })

    LoadCompilerRule: (rule) => @_compiler\LoadRules(rule)
    CacheDatas: => for i in *@_datas do i\Cache!

    GetSrcIndex: (name) =>
        for k, v in ipairs(@_member)
            if v[1] == name
                return k
    GetDataIndex: (name) =>
        for k, v in ipairs(@_datas)
            if v[1] == name
                return k

    Judge: (namei, probi) =>
        cntprob = @_datas[probi][2]
        @_member[namei][3][probi] = {}
        cntret = @_member[namei][3][probi]
        @_InitRet(cntret, #cntprob.datas)
        srcs = fs.glob("*", @_member[namei][2])
        target = @_compiler\Select(@_datas[probi][1], srcs)
        if not @_WriteSearchInfo(cntret, target)
            return cntret

        exeret = @_compiler\Compile(target)
        if not @_WriteComplieInfo(cntret, exeret)
            return cntret

        tmp_files = {}
        createtemp = ->
            name = os.tmpname!
            f = io.open(name, "w")
            f\close!
            table.insert(tmp_files, name)
            return name

        for k, data in pairs(cntprob.datas)
            tmpout = createtemp!
            runret = runner.Run({
                max_cpu_time: cntprob.max_cpu_time,
                max_real_time:cntprob.max_cpu_time,
                max_memory: cntprob.max_memory,
                exe_path: exeret.exe_path,
                args: { exeret.exe_path },
                input_path: data[1],
                output_path: tmpout
            })
            continue if not @_WriteRunnerInfo(cntret.detail[k], runret)

            cmpret = cntprob.judge_func(io.open(data[2]), io.open(tmpout), io.open(data[1]))
            continue if not @_WriteJudgerInfo(cntret.detail[k], cmpret)

            cntret.detail[k].flag  = "ACCEPT"
            cntret.detail[k].score = data[3]
            cntret.score += data[3]

        for v in *tmp_files
            os.remove(v)
        return cntret

    _WriteSearchInfo: (ret, file) =>
        if file
            ret.size = fs.fsize(file)
            ret.lang = string.sub(select(2, splitext(file)), 2)
            return true
        else
            ret.lang = "none"
            for v in *ret.detail
                v.flag = "FILE_NOT_FOUND"

    _WriteComplieInfo: (ret, cpret) =>
        ret.cpmsg = cpret.error_info
        return true if cpret.success

        for v in *ret.detail
            v.flag = "COMPLETION_ERROR"
        return false

    _WriteRunnerInfo: (ret, runret) =>
        with ret
            .cpu_time = runret.cpu_time
            .real_time = runret.real_time
            .memory = runret.memory
            .exit_code = runret.exit_code

        return true if runret.flag == runner._RESULT_SUCCESS

        for k, v in pairs(runner)
            ret.flag = string.sub(k, 2) if v == runret.flag
            break if v == runret.flag
        return false

    _WriteJudgerInfo: (ret, cmpret) =>
        ret.extra = cmpret.exeret
        ret.flag = "WRONG_ANSWER" if not cmpret.ac
        return cmpret.ac

    _InitRet: (ret, num) =>
        with ret
            .lang = ""
            .cpmsg = ""
            .size = 0
            .score = 0
            .detail = {}

        for i = 1, num
            ret.detail[i] = {
                flag: "",
                cpu_time: 0,
                real_time: 0,
                memory: 0,
                exit_code: 0,
                extra: "",
                score: 0
            }

return contest
